﻿///////////////////////////////////////////////////////////////////////////////
// Copyright 2016 lingzimaike@gmail.com
// GPLv2
// Author: lingzimaike
///////////////////////////////////////////////////////////////////////////////

#ifndef PAPA_NET_TCP_SOCKET_H_
#define PAPA_NET_TCP_SOCKET_H_

#include <cstddef>
#include <cstdint>
#include <string>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <boost/scoped_ptr.hpp>
#include <papa/base.h>

namespace papa {
namespace net {

	class SocketEventHandler;
	class IORunnable;
	class TcpServer;

	class TcpSocket
	{
	public:
		TcpSocket(SocketEventHandler* handler);
		TcpSocket(::boost::scoped_ptr<SocketEventHandler>& handler);
		~TcpSocket(void);

		bool Open(evutil_socket_t sock, struct event_base* base, 
			struct sockaddr* sa, int socklen);
		void Close(void);

		int get_id(void) const { return id_; }
		const ::std::string& get_host(void) const { return host_; }
		int get_port(void) const { return port_; }
		int get_error_code(void) const { return error_code_; }
		const ::std::string& get_error_string(void) const { return error_string_; }
		void is_fatal_error(bool fatal_error) { fatal_error_ = fatal_error; }
		bool is_fatal_error(void) const { return fatal_error_; }

		void Send(const ::std::uint8_t* buf, ::std::size_t size);
		::std::size_t Recv(::std::uint8_t* buf, ::std::size_t size);
		::std::size_t RecvLength(void) const;
		::std::size_t RecvPeek(::std::uint8_t* buf, ::std::size_t size);
		::std::size_t RecvSkip(::std::size_t size);

		static void CloseSocket(evutil_socket_t sock);

	private:
		friend class TcpServer;

		void do_close(void);
		void on_eof_or_error(void);

		static void conn_readcb(struct ::bufferevent *, void *);
		static void conn_writecb(struct ::bufferevent *, void *);
		static void conn_eventcb(struct ::bufferevent *, short, void *);

		int id_;
		evutil_socket_t sock_;
		struct ::bufferevent* bev_;
		IORunnable* actor_;
		TcpServer* owner_;
		::boost::scoped_ptr<SocketEventHandler> event_handler_;
		::std::string host_;
		int port_;
		::std::string error_string_;
		int error_code_;
		volatile bool fatal_error_;

		PAPA_DISALLOW_COPY_AND_ASSIGN(TcpSocket)
	};

}}  // namespace papa::net

#endif // PAPA_NET_TCP_SOCKET_H_
